/*
 * jQuery Plugin: Tokenizing Autocomplete Text Entry
 * Version 1.1
 *
 * Copyright (c) 2009 James Smith (http://loopj.com)
 * Licensed jointly under the GPL and MIT licenses,
 * choose which one suits your project best!
 *
 */
(function($) {

    $.fn.tokenInput = function (url, options) {
        var settings = $.extend({
            url: url,
            hintText: "Type in a search term",
            noResultsText: "No results",
            searchingText: "Searching...",
            searchDelay: 300,
            minChars: 1,
            tokenLimit: 20,
            tokenLimitText: "Bạn chỉ được đánh dấu tối đa %tokenLimit% người.",
            jsonContainer: null,
            method: "GET",
            contentType: "json",
            queryParam: "q",
            additionParam: {},
            onResult: null,
            selector: this
        }, options);
        
        settings.classes = $.extend({
            tokenList: "token-input-list",
            token: "token-input-token",
            tokenDelete: "token-input-delete-token",
            selectedToken: "token-input-selected-token",
            highlightedToken: "token-input-highlighted-token",
            dropdown: "token-input-dropdown",
            dropdownItem: "token-input-dropdown-item",
            dropdownItem2: "token-input-dropdown-item2",
            selectedDropdownItem: "token-input-selected-dropdown-item",
            inputToken: "token-input-input-token"
        }, options.classes);

        return this.each(function () {
            var list = new $.TokenList(this, settings);
        });
    };

    $.TokenList = function (input, settings) {
        /*
        // Variables
        //

        // Input box position "enum"*/
        var POSITION = {
            BEFORE: 0,
            AFTER: 1,
            END: 2
        };

        /* Keys "enum"*/
        var KEY = {
            BACKSPACE: 8,
            TAB: 9,
            RETURN: 13,
            ESC: 27,
            LEFT: 37,
            UP: 38,
            RIGHT: 39,
            DOWN: 40,
            COMMA: 188
        };

        /* Save the tokens*/
        var saved_tokens = [];
    
        /* Keep track of the number of tokens in the list*/
        var token_count = 0;

        /* Basic cache to save on db hits*/
        var cache = new $.TokenList.Cache();

        /* Keep track of the timeout*/
        var timeout;

        /* Create a new text input an attach keyup events*/
        var input_box = $("<input type=\"text\">")
        .css({
            outline: "none"
        })
        .focus(function () {
            if (settings.tokenLimit == null || settings.tokenLimit != token_count) {
                show_dropdown_hint();
            }
        })
        .clickoutside(function () {
            hide_dropdown();
        })
        .keydown(function (event) {
            var previous_token;
            var next_token;

            switch(event.keyCode) {
                case KEY.LEFT:
                    break;
                case KEY.RIGHT:
                    break;
                case KEY.UP:
                    if ( $("div.token-input-dropdown-facebook").css('display') == 'none' ) {
                        return false;
                    }
                case KEY.DOWN:
                    if ( $("div.token-input-dropdown-facebook").css('display') == 'none' ) {
                        return false;
                    }
                    if(!$(this).val()) {
                        previous_token = input_token.prev();
                        next_token = input_token.next();

                        if((previous_token.length && previous_token.get(0) === selected_token) || (next_token.length && next_token.get(0) === selected_token)) {
                            /* Check if there is a previous/next token and it is selected*/
                            if(event.keyCode == KEY.LEFT || event.keyCode == KEY.UP) {
                                deselect_token($(selected_token), POSITION.BEFORE);
                            } else {
                                deselect_token($(selected_token), POSITION.AFTER);                                
                            }
                        } else if((event.keyCode == KEY.LEFT || event.keyCode == KEY.UP) && previous_token.length) {
                            /* We are moving left, select the previous token if it exists*/
                            select_token($(previous_token.get(0)));
                        } else if((event.keyCode == KEY.RIGHT || event.keyCode == KEY.DOWN) && next_token.length) {
                            /* We are moving right, select the next token if it exists  */                          
                            select_token($(next_token.get(0)));                            
                        }
                    } else {
                        var dropdown_item = null;

                        if(event.keyCode == KEY.DOWN || event.keyCode == KEY.RIGHT) {
                            dropdown_item = $(selected_dropdown_item).next();                            
                            /*16px*/
                        } else {
                            dropdown_item = $(selected_dropdown_item).prev();
                        }

                        if(dropdown_item.length) {
                            select_dropdown_item(dropdown_item);
                        }
                        return false;
                    }
                    break;

                case KEY.BACKSPACE:
                    previous_token = input_token.prev();

                    if(!$(this).val().length) {
                        if(selected_token) {
                            delete_token($(selected_token));
                        } else if(previous_token.length) {
                            select_token($(previous_token.get(0)));
                        }

                        return false;
                    } else if($(this).val().length == 1) {
                        hide_dropdown();
                    } else {
                        /* set a timeout just long enough to let this function finish.*/
                        setTimeout(function(){
                            do_search(false);
                        }, 5);
                    }
                    break;

                case KEY.TAB:
                case KEY.RETURN:
                case KEY.COMMA:
                    if(selected_dropdown_item) {
                        add_token($(selected_dropdown_item));
                        return false;
                    }
                    break;

                case KEY.ESC:
                    hide_dropdown();
                    return true;

                default:
                    if(is_printable_character(event.keyCode)) {
                        /* set a timeout just long enough to let this function finish.*/
                        setTimeout(function(){
                            do_search(false);
                        }, 5);
                    }
                    break;
            }
        });

        /* Keep a reference to the original input box*/
        var hidden_input = $(input)
        .hide()
        .focus(function () {
            input_box.focus();
        })
        .blur(function () {
            input_box.blur();
        });

        /* Keep a reference to the selected token and dropdown item*/
        var selected_token = null;
        var selected_dropdown_item = null;

        /* The list to store the token items in*/
        var token_list = $("<ul />")
        .addClass(settings.classes.tokenList)
        .insertAfter(hidden_input)
        .click(function (event) {
            var li = get_element_from_event(event, "li");
            if(li && li.get(0) != input_token.get(0)) {
                toggle_select_token(li);
                return false;
            } else {
                input_box.focus();

                if(selected_token) {
                    deselect_token($(selected_token), POSITION.END);
                }
            }
        })
        .mouseover(function (event) {
            var li = get_element_from_event(event, "li");
            if(li && selected_token !== this) {
                li.addClass(settings.classes.highlightedToken);
            }
        })
        .mouseout(function (event) {
            var li = get_element_from_event(event, "li");
            if(li && selected_token !== this) {
                li.removeClass(settings.classes.highlightedToken);
            }
        })
        .mousedown(function (event) {
            /* Stop user selecting text on tokens       */     
            var li = get_element_from_event(event, "li");
            if(li){
                return false;
            }
        });


        /* The list to store the dropdown items in*/
        var dropdown = $("<div>")
        .addClass(settings.classes.dropdown)
        .insertAfter(token_list)
        .hide();

        /* The token holding the input box*/
        var input_token = $("<li />")
        .addClass(settings.classes.inputToken)
        .appendTo(token_list)
        .append(input_box);

        init_list();

        /*
        // Functions
        //


        // Pre-populate list if items exist*/
        function init_list () {
            li_data = settings.prePopulate;            
            if(li_data && li_data.length) {
                for(var i in li_data) {
                    var this_token = $("<li><p>"+li_data[i].name+"</p> </li>")
                    .addClass(settings.classes.token)
                    .insertBefore(input_token);                    
                    $("<span>x</span>")
                    .addClass(settings.classes.tokenDelete)
                    .appendTo(this_token)
                    .click(function () {
                        delete_token($(this).parent());
                        return false;
                    });

                    $.data(this_token.get(0), "tokeninput", {
                        "id": li_data[i].id,
                        "name": li_data[i].name
                    });

                    /* Clear input box and make sure it keeps focus*/
                    input_box
                    .val("")
                    .focus();

                    /* Don't show the help dropdown, they've got the idea*/
                    hide_dropdown();

                    /* Save this token id */                   
                    var id_string = li_data[i].id + ","
                    hidden_input.val(hidden_input.val() + id_string);

                    /*@nguyennb: increaste token_count by 1*/
                    token_count++;
                }
            }
        }

        function is_printable_character(keycode) {
            if((keycode >= 48 && keycode <= 90) ||      /* 0-1a-z*/
            (keycode >= 96 && keycode <= 111) ||     /* numpad 0-9 + - / * .*/
            (keycode >= 186 && keycode <= 192) ||    /* ; = , - . / ^*/
            (keycode >= 219 && keycode <= 222)       /* ( \ ) '*/
        ) {
                return true;
            } else {
                return false;
            }
        }

        /* Get an element of a particular type from an event (click/mouseover etc)*/
        function get_element_from_event (event, element_type) {
            var target = $(event.target);
            var element = null;

            if(target.is(element_type)) {
                element = target;
            } else if(target.parent(element_type).length) {
                element = target.parent(element_type+":first");
            }

            return element;
        }

        /* Inner function to a token to the list*/
        function insert_token(id, value) {            
            var this_token = $("<li><p>"+ value +"</p> </li>")
            .addClass(settings.classes.token)
            .insertBefore(input_token);

            /* The 'delete token' button*/
            $("<span>x</span>")
            .addClass(settings.classes.tokenDelete)
            .appendTo(this_token)
            .click(function () {
                delete_token($(this).parent());
                return false;
            });

            $.data(this_token.get(0), "tokeninput", {
                "id": id,
                "name": value
            });

            return this_token;
        }

        /* Add a token to the token list based on user input*/
        function add_token (item) {
            var li_data = $.data(item.get(0), "tokeninput");            
            var this_token = insert_token(li_data.id, li_data.name);

            /* Clear input box and make sure it keeps focus*/
            input_box
            .val("")
            .focus();

            /* Don't show the help dropdown, they've got the idea*/
            hide_dropdown();

            /* Save this token id*/
            var id_string = li_data.id + ","
            hidden_input.val(hidden_input.val() + id_string);
        
            token_count++;
            /*@nguyennb: ???
//            if(settings.tokenLimit != null && settings.tokenLimit >= token_count) {
//                input_box.hide();
//                hide_dropdown();
//            }    */        
        }

        /* Select a token in the token list*/
        function select_token (token) {
            token.addClass(settings.classes.selectedToken);
            selected_token = token.get(0);

            /* Hide input box*/
            input_box.val("");

            /* Hide dropdown if it is visible (eg if we clicked to select token)*/
            hide_dropdown();
        }

        /* Deselect a token in the token list*/
        function deselect_token (token, position) {
            token.removeClass(settings.classes.selectedToken);
            selected_token = null;

            if(position == POSITION.BEFORE) {
                input_token.insertBefore(token);
            } else if(position == POSITION.AFTER) {
                input_token.insertAfter(token);
            } else {
                input_token.appendTo(token_list);
            }

            /* Show the input box and give it focus again*/
            input_box.focus();
        }

        /* Toggle selection of a token in the token list*/
        function toggle_select_token (token) {
            if(selected_token == token.get(0)) {
                deselect_token(token, POSITION.END);
            } else {
                if(selected_token) {
                    deselect_token($(selected_token), POSITION.END);
                }
                select_token(token);
            }
        }

        /* Delete a token from the token list*/
        function delete_token (token) {
            /* Remove the id from the saved list*/
            var token_data = $.data(token.get(0), "tokeninput");

            /* Delete the token*/
            token.remove();
            selected_token = null;

            /* Show the input box and give it focus again*/
            input_box.focus();

            /* Delete this token's id from hidden input*/
            var str = hidden_input.val()
            var start = str.indexOf(token_data.id+",");
            var end = str.indexOf(",", start) + 1;

            if(end >= str.length) {
                hidden_input.val(str.slice(0, start));
            } else {
                hidden_input.val(str.slice(0, start) + str.slice(end, str.length));
            }
        
            token_count--;
        
            if (settings.tokenLimit != null) {
                input_box
                .show()
                .val("")
                .focus();
            }
        }

        /* Hide and clear the results dropdown*/
        function hide_dropdown () {
            dropdown.hide().empty();
            selected_dropdown_item = null;
        }

        function show_dropdown_searching () {
            dropdown
            .html("<p>"+settings.searchingText+"</p>")
            .show();
        }

        function show_dropdown_hint () {
            dropdown
            .html("<p>"+settings.hintText+"</p>")
            .show();
        }

        /* Highlight the query part of the search term*/
        function highlight_term(value, term) {
            return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>");
        }

        /* Populate the results dropdown with some results*/
        function populate_dropdown (query, results) {
            
            if(results.length) {
                dropdown.empty();
                var dropdown_ul = $("<ul>")
                .appendTo(dropdown)
                .mouseover(function (event) {
                    select_dropdown_item(get_element_from_event(event, "li"));
                })
                .mousedown(function (event) {
                    add_token(get_element_from_event(event, "li"));
                    return false;
                })
                .hide();

                for(var i in results) {
                    if (results.hasOwnProperty(i)) {
                        var this_li = $("<li>"+highlight_term(results[i].name, query)+"</li>")
                        .appendTo(dropdown_ul);

                        if(i%2) {
                            this_li.addClass(settings.classes.dropdownItem);
                        } else {
                            this_li.addClass(settings.classes.dropdownItem2);
                        }

                        if(i == 0) {
                            select_dropdown_item(this_li);
                        }

                        $.data(this_li.get(0), "tokeninput", {
                            "id": results[i].id,
                            "name": results[i].name
                        });
                    }
                }

                dropdown.show();
                dropdown_ul.slideDown("fast");

            } else {
                dropdown
                .html("<p>"+settings.noResultsText+"</p>")
                .show();
            }
        }

        /* Highlight an item in the results dropdown*/
        function select_dropdown_item (item) {
            if(item) {
                if(selected_dropdown_item) {
                    deselect_dropdown_item($(selected_dropdown_item));
                }

                item.addClass(settings.classes.selectedDropdownItem);
                selected_dropdown_item = item.get(0);
            }
        }

        /* Remove highlighting from an item in the results dropdown*/
        function deselect_dropdown_item (item) {
            item.removeClass(settings.classes.selectedDropdownItem);
            selected_dropdown_item = null;
        }

        /* Do a search and show the "searching" dropdown if the input is longer*/
        // than settings.minChars*/
        function do_search(immediate) {
            var query = input_box.val().toLowerCase();

            if (query && query.length) {
                if(selected_token) {
                    deselect_token($(selected_token), POSITION.AFTER);
                }
                if (query.length >= settings.minChars) {
                    show_dropdown_searching();
                    if (immediate) {
                        run_search(query);
                    } else {
                        clearTimeout(timeout);
                        timeout = setTimeout(function(){
                            run_search(query);
                        }, settings.searchDelay);
                    }
                } else {
                    hide_dropdown();
                }
            }
        }

        /* Do the actual search*/
        function run_search(query) {
            if (settings.tokenLimit) {                
                /*var tagged = $(settings.selector).val().split(',');
                //var totalTagged = tagged.length - 1;
                //if (totalTagged >= settings.tokenLimit) {*/
                if (token_count >= settings.tokenLimit) {                    
                    dropdown
                    .html("<p>"+settings.tokenLimitText.replace('%tokenLimit%', settings.tokenLimit)+"</p>")
                    .show();
                    return true;
                }
            }

            /*Url decode string*/
            query = tolower(query);
            query = htmlEncode(query);            
            
            var additionParam = '';
            try {
                for (key in settings.additionParam) {
                    value = eval(settings.additionParam[key]);
                    additionParam += "&" + key + "=" + value;
                }
            } catch(error) {
                value = settings.additionParam[key];
                additionParam += "&" + key + "=" + value;
            }            

            query += additionParam;
            var cached_results = cache.get(query);
            if(cached_results) {
                populate_dropdown(query, cached_results);
            } else {
                var queryStringDelimiter = settings.url.indexOf("?") < 0 ? "?" : "&";
                var callback = function(results) {
                    if($.isFunction(settings.onResult)) {
                        results = settings.onResult.call(this, results);
                    }
                    cache.add(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
                    populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
                };
            
                if(settings.method == "POST") {
                    $.post(settings.url + queryStringDelimiter + settings.queryParam + "=" + query, {}, callback, settings.contentType);
                } else {
                    $.get(settings.url + queryStringDelimiter + settings.queryParam + "=" + query, {}, callback, settings.contentType);
                }
            }
        }
    };

    /* Really basic cache for the results*/
    $.TokenList.Cache = function (options) {
        var settings = $.extend({
            max_size: 50
        }, options);

        var data = {};
        var size = 0;

        var flush = function () {
            data = {};
            size = 0;
        };

        this.add = function (query, results) {
            if(size > settings.max_size) {
                flush();
            }

            if(!data[query]) {
                size++;
            }

            data[query] = results;
        };

        this.get = function (query) {
            return data[query];
        };
    };

    /*Replace all character*/
    function replaceAll(strSource, strTarget, strSubString)
    {
        var strText = strSource;
        var intIndexOfMatch = strText.indexOf( strTarget );
        while (intIndexOfMatch != -1)
        {
            strText = strText.replace( strTarget, strSubString )
            intIndexOfMatch = strText.indexOf( strTarget );
        }
        return strText ;
    };

    /*Html encode*/
    function htmlEncode(textDeco)
    {
        var resultText = textDeco;
        resultText = trim(resultText);
        resultText = replaceAll(resultText,"\r", "");
        resultText = replaceAll(resultText,"\n", "<br>");
        resultText = encodeURIComponent(resultText);
        return resultText;
    };

    /*To lower string*/
    function tolower(textDeco)
    {
        var resultText = textDeco;

        /*Replace UTF8 Vietnamese*/
        resultText = replaceAll(resultText, "À", "à");
        resultText = replaceAll(resultText, "Á", "á");
        resultText = replaceAll(resultText, "Ả", "ả");
        resultText = replaceAll(resultText, "Ã", "ã");
        resultText = replaceAll(resultText, "Ạ", "ạ");
        resultText = replaceAll(resultText, "Ă", "ă");
        resultText = replaceAll(resultText, "Ằ", "ằ");
        resultText = replaceAll(resultText, "Ắ", "ắ");
        resultText = replaceAll(resultText, "Ẳ", "ẳ");
        resultText = replaceAll(resultText, "Ẵ", "ẵ");
        resultText = replaceAll(resultText, "Ặ", "ặ");
        resultText = replaceAll(resultText, "Â", "â");
        resultText = replaceAll(resultText, "Ầ", "ầ");
        resultText = replaceAll(resultText, "Ấ", "ấ");
        resultText = replaceAll(resultText, "Ẩ", "ẩ");
        resultText = replaceAll(resultText, "Ẫ", "ẫ");
        resultText = replaceAll(resultText, "Ậ", "ậ");
        resultText = replaceAll(resultText, "Đ", "đ");
        resultText = replaceAll(resultText, "È", "è");
        resultText = replaceAll(resultText, "É", "é");
        resultText = replaceAll(resultText, "Ẻ", "ẻ");
        resultText = replaceAll(resultText, "Ẽ", "ẽ");
        resultText = replaceAll(resultText, "Ẹ", "ẹ");
        resultText = replaceAll(resultText, "Ê", "ê");
        resultText = replaceAll(resultText, "Ề", "ề");
        resultText = replaceAll(resultText, "Ế", "ế");
        resultText = replaceAll(resultText, "Ể", "ể");
        resultText = replaceAll(resultText, "Ễ", "ễ");
        resultText = replaceAll(resultText, "Ệ", "ệ");
        resultText = replaceAll(resultText, "Ì", "ì");
        resultText = replaceAll(resultText, "Í", "í");
        resultText = replaceAll(resultText, "Ỉ", "ỉ");
        resultText = replaceAll(resultText, "Ĩ", "ĩ");
        resultText = replaceAll(resultText, "Ị", "ị");
        resultText = replaceAll(resultText, "Ò", "ò");
        resultText = replaceAll(resultText, "Ó", "ó");
        resultText = replaceAll(resultText, "Ỏ", "ỏ");
        resultText = replaceAll(resultText, "Õ", "õ");
        resultText = replaceAll(resultText, "Ọ", "ọ");
        resultText = replaceAll(resultText, "Ô", "ô");
        resultText = replaceAll(resultText, "Ồ", "ồ");
        resultText = replaceAll(resultText, "Ố", "ố");
        resultText = replaceAll(resultText, "Ổ", "ổ");
        resultText = replaceAll(resultText, "Ỗ", "ỗ");
        resultText = replaceAll(resultText, "Ộ", "ộ");
        resultText = replaceAll(resultText, "Ơ", "ơ");
        resultText = replaceAll(resultText, "Ờ", "ờ");
        resultText = replaceAll(resultText, "Ớ", "ớ");
        resultText = replaceAll(resultText, "Ở", "ở");
        resultText = replaceAll(resultText, "Ỡ", "ỡ");
        resultText = replaceAll(resultText, "Ợ", "ợ");
        resultText = replaceAll(resultText, "Ù", "ù");
        resultText = replaceAll(resultText, "Ú", "ú");
        resultText = replaceAll(resultText, "Ủ", "ủ");
        resultText = replaceAll(resultText, "Ũ", "ũ");
        resultText = replaceAll(resultText, "Ụ", "ụ");
        resultText = replaceAll(resultText, "Ư", "ư");
        resultText = replaceAll(resultText, "Ừ", "ừ");
        resultText = replaceAll(resultText, "Ứ", "ứ");
        resultText = replaceAll(resultText, "Ử", "ử");
        resultText = replaceAll(resultText, "Ữ", "ữ");
        resultText = replaceAll(resultText, "Ự", "ự");
        resultText = replaceAll(resultText, "Ỳ", "ỳ");
        resultText = replaceAll(resultText, "Ý", "ý");
        resultText = replaceAll(resultText, "Ỷ", "ỷ");
        resultText = replaceAll(resultText, "Ỹ", "ỹ");
        resultText = replaceAll(resultText, "Ỵ", "ỵ");
        resultText = replaceAll(resultText, "ð", "d");
        resultText = replaceAll(resultText, "ñ", "n");
        resultText = replaceAll(resultText, "ø", "o");
        resultText = replaceAll(resultText, "œ", "oe");
        resultText = replaceAll(resultText, "ß", "ss");
        resultText = replaceAll(resultText, "þ", "th");
        resultText = replaceAll(resultText, "æ", "ae");
        resultText = replaceAll(resultText, "ç", "c");
        resultText = replaceAll(resultText, "Ø", "o");
        resultText = replaceAll(resultText, "Œ", "oe");
        resultText = replaceAll(resultText, "Þ", "th");
        resultText = replaceAll(resultText, "ç", "c");
        resultText = replaceAll(resultText, "\uFB00", "ff");
        resultText = replaceAll(resultText, "\uFB01", "fi");
        resultText = replaceAll(resultText, "\uFB02", "fl");
        resultText = replaceAll(resultText, "\uFB03", "ffi");
        resultText = replaceAll(resultText, "\uFB04", "ffl");
        resultText = replaceAll(resultText, "\uFB05", "ft");
        resultText = replaceAll(resultText, "\uFB06", "st");

        return resultText.toLowerCase();
    };

    /*Trim string*/
    function trim(textDeco)
    {
        var whitespace = ' \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000';

        for(var i = 0; i < textDeco.length; i++)
        {
            if (whitespace.indexOf(textDeco.charAt(i)) === -1)
            {
                textDeco = textDeco.substring(i);
                break;
            }
        }

        for(i = textDeco.length - 1; i >= 0; i--)
        {
            if (whitespace.indexOf(textDeco.charAt(i)) === -1)
            {
                textDeco = textDeco.substring(0, i + 1);
                break;
            }
        }

        return whitespace.indexOf(textDeco.charAt(0)) === -1 ? textDeco : '';
    };

})(jQuery);

